#pragma once
#ifndef __G_THREAD_H__
#define __G_THREAD_H__

#include <atomic>

#include "GTypes.h"
#include "GDebug.h"
#include "GSynchronizer.h"

// Note: Can only be used on static and extern members/globals.
#ifdef G_PLATFORM_WINDOWS
	#define G_THREAD_LOCAL	__declspec(thread)
#else
	#define G_THREAD_LOCAL	__declspec(__thread)
#endif

namespace GThread
{
	enum GThreadPriority
	{
		G_TP_HIGHEST	= 0,
		G_TP_HIGH		= 1,
		G_TP_NORMAL		= 2,
		G_TP_LOW		= 3,
		G_TP_LOWEST		= 4,
		G_TP_DEFAULT	= G_TP_NORMAL,
	};

	enum GThreadState
	{
		G_TS_RELEASED		= 0,
		G_TS_SLEEPING		= 1,
		G_TS_PAUSED			= 2,
		G_TS_RUNNING		= 3,
		G_TS_TERMINATED		= 4,
	};

	typedef	GUInt32 (_stdcall *GEntryMethod)(void* Parameters);




	class GThread : public GWaitable
	{
	G_DECLARE_DEBUG_NAME();

	typedef std::atomic<GThreadState> AtomicState;
	typedef std::atomic<GThreadPriority> AtomicPriority;
	
	friend GUInt32 _stdcall ThreadEntryMethod(void* Parameters);

	protected:		
		GUInt32				Id;
		AtomicState			State;
		AtomicPriority		Priority;
		GEntryMethod		EntryMethod;

		void*				Handle;
		void*				Parameters;

	public:
							GThread();
							~GThread();

		void*				GetHandle() const;

		GUInt32				GetId() const;
		GThreadState		GetState() const;
		void*				GetParameters() const;
		GEntryMethod		GetEntryMethod() const;

		bool				Pause() PLATFORM;
		bool				Resume() PLATFORM;

		bool				DidExit(OUTPUT GUInt32& ExitCode) PLATFORM;

		bool				SetPriority(GThreadPriority NewPriority) PLATFORM;
		GThreadPriority		GetPriority() const;

		bool				IsCreated() const;
		bool				IsRunning() const;

		bool				Create(bool StartSuspended, GEntryMethod Entry, void* Params = NULL, GSize StackSize = 0) PLATFORM;
		bool				Create(void* Handle) PLATFORM;
		void				Release(bool WaitTillExit) PLATFORM;

		static bool			Sleep(GUInt32 TimeOut) PLATFORM;

		static void			SwitchThread() PLATFORM;
		static GThread*		GetCurrentThread();
		static GUInt32		GetCurrentThreadId() PLATFORM;

	};
	
}

#define mGCurrentThread		GThread::GThread::GetCurrentThread()

#endif